#include "Sphere.h"
#include <iostream>
//#include "Texture2.h"

using namespace std;

Symbol WSphere::parse(ifstream &file, Symbol curToken) {
    float *numFill;

    curToken = matchToken(file, curToken, LBrace);

    numFill = parseVector(file, curToken, &curToken, 3);
    center = vec3(numFill[0], numFill[1], numFill[2]);
    delete numFill;

    curToken = matchToken(file, curToken, Comma);
    if (curToken.isToken) {
        cout << "expected number, found " 
            << tokenList[curToken.t] << endl;
        exit(1);
    }

    radius = curToken.num;
    curToken = nextToken(file);

    curToken = parseMaterials(file, curToken, this);
    return curToken;
}

bool WSphere::intersect(Ray *r) {
    vec3 PtoC, point, d;
    vec4 pprime, dprime;
    float A;
    float B;
    float C;
    float t, t1, t2;
    float deter;

    pprime = transform * vec4(r->point.x, r->point.y, r->point.z, 1.0);
    dprime = transform * vec4(r->d.x, r->d.y, r->d.z, 0.0);
    point = vec3(pprime.x, pprime.y, pprime.z);
    d = vec3(dprime.x, dprime.y, dprime.z);

    PtoC = point - center;
    A = dot(d, d);
    B = 2 * dot(d, PtoC);
    C = dot(PtoC, PtoC) - radius * radius;

    deter = B * B - 4 * A * C;

    if (deter < 0) {
        return false;
    }

    t1 = (-B + sqrt(deter))/(2 * A);
    t2 = (-B - sqrt(deter))/(2 * A);
    if (t2 > 0.001 && t2 < t1) {
        t = t2;
    }
    else {
        t = t1;
    }

    if (t > 0 && t < r->t) {
        r->t = t;
        return true;
    }

    return false;
}

vec3 WSphere::genRandomPoint(vec3 look, int cur, int lim) {
    vec4 point;
//    vec3 loc = normalize(look - center);
//    vec3 axis = cross(vec3(0, 1, 0), loc);
//    int split = sqrt((float) lim);
    float theta, phi;
//    int phiLevel = cur / split;
//    int thetaLevel = cur % split;

    theta = 2.f * 3.1415926f * ((float) rand()) / RAND_MAX;
//    theta = 3.1415926 / split * (2.f * thetaLevel + 1);
    phi = 3.1415926f * ((float) rand()) / RAND_MAX - 3.1415926f / 2.f;
//    phi = (3.1415926 / split) * (phiLevel * 0.5 + 0.25);

    point.x = radius * sin(theta) * cos(phi) + center.x;
    point.y = radius * sin(theta) * sin(phi) + center.y;
    point.z = radius * cos(phi) + center.z;
    point.w = 1.f;

    point = transpose(transform) * point;

    return vec3(point.x, point.y, point.z);
}

vec3 WSphere::getNormal(Ray *r) {
    float theta, phi;
    vec3 point, d, n;
    vec4 pprime, dprime, normal;
    vec2 randNormal;
    vec3 fixNormal;
    vec3 axis, up;

    pprime = transform * vec4(r->point.x, r->point.y, r->point.z, 1.0);
    dprime = transform * vec4(r->d.x, r->d.y, r->d.z, 0.0);
    point = vec3(pprime.x, pprime.y, pprime.z);
    d = vec3(dprime.x, dprime.y, dprime.z);

    n = point + r->t * d - center;
    normal = transpose(transform) * vec4(n.x, n.y, n.z, 0.0);

    /*theta = acos(n.z / radius);
    phi = acos(n.x / radius / sin(theta));

    theta = 180.0 * theta / PI;
    phi = 180.0 * phi / PI;

    randNormal.x = sin(noise->turbulence(theta, phi, 64.0));
    randNormal.y = (noise->turbulence(theta / 12.0, phi / 12.0, 64.0)) * 0.85;
*/

    theta = acos(n.z / radius);
    phi = asin(n.y / radius / sin(theta)) * 180.0 / PI + 90.0; 
    theta = theta * 180.0 / PI + 180.0;
    if (phi != phi) {
        phi = 90.0;
    }

    theta = (theta / 180.0 - 1.0) * 100.0;
    phi = (phi / 180.0 - 1.0) * 200.0;

    /*fixNormal.x = fabs(noise->noise(theta * 10.0 / 4, phi * 10.0 / 4)) - 0.25;
    fixNormal.y = fabs(noise->noise(theta * 20.0 / 8, phi * 20.0 / 9)) - 0.25;
    fixNormal.z = fabs(noise->noise(theta * 30.0 / 3, phi * 30.0 / 3)) - 0.25;*/
    /*fixNormal.x = theta / 20 + phi * 2 / 20.0 + noise->noise(n.x, n.y) / 20;
    fixNormal.y = 0;
    fixNormal.z = fabs(sin(fixNormal.x * PI));
    fixNormal.x = fabs(sin(fixNormal.x * PI));*/
    //printf("<%f, %f, %f>\n", fixNormal.x, fixNormal.y, fixNormal.z);

    /*up = vec3(0, 1, 0);
    n = normalize(vec3(normal.x, normal.y, normal.z));
    axis = cross(n, up);

    if (length(axis) < 0.1) {
        return vec3(fixNormal.x, fixNormal.y, fixNormal.z);
    }

    theta = -acos(dot(up, n)) * 180.0 / PI;
    fixNormal = rotate(mat4(1.0), theta, axis) * fixNormal;*/
    switch ((int) finish.bump) {
        case 1:
            fixNormal = perlin1(theta, phi, n, noise);
            break;
        case 2:
            fixNormal = perlin2(theta, phi, n, noise);
            break;
        case 3:
            fixNormal = perlin3(theta, phi, n, noise);
            break;
        default:
            fixNormal = vec3(0); 
            break;
    }

    return normalize(vec3(fixNormal.x + normal.x, 
                          fixNormal.y + normal.y,
                          fixNormal.z + normal.z));
}

AABB *WSphere::generateAABB() {
    AABB *ret;

    ret = new AABB();
    ret->g = this;
    ret->isLeaf = true;

    ret->min = center - vec3(radius, radius, radius);
    ret->max = center + vec3(radius, radius, radius);

    fix_AABB_transform(ret, vtrans);

    return ret;
}
